/**************************************************************************************
*
*	Copyright (C) 2010 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	Renderer.cpp
*
*	Comments	-	See Renderer.h
*
**************************************************************************************/

#ifdef __PSX_WINDOWS_WINDOW

#include "../Include/Renderer.h"
#include "../Include/RendererDevice.h"
#include "../Include/Engine.h"
#include "../Include/Texture.h"
#include "../Include/GraphicsResourceManager.h"
#include "../Include/ResourceManager.h"
#include "../Include/DX9Mapping.h"

#pragma comment( lib, "d3d9.lib" )

namespace Pulse
{

	Renderer::Renderer( void )
	{

	}

	Renderer::~Renderer( void )
	{
		PSX_SafeRelease( m_pD3D );
	}

	EErrorCode::Type Renderer::Initialize( void )
	{
		m_pD3D = Direct3DCreate9( D3D_SDK_VERSION );

		if ( !m_pD3D )
		{
			PSX_PushError( "Failed to create Direct3D 9 object." );
			return EErrorCode::GRAPHICS;
		}

		BuildDisplayModeList();

		return EErrorCode::NONE;
	}

	void Renderer::Cleanup( void )
	{
		m_displayModes.Clear();

		// delete rendering stages
		// TODO: Delete dynamically allocated rendering stages first then clear
		m_renderStages.Clear();

		PSX_SafeRelease( m_pD3D );
	}

	void Renderer::CreateDevice( void )
	{
		Engine *pEngine = Engine::GetInstance();
		const EngineSettings *pSettings = pEngine->GetEngineSettings();

		PSX_ZeroMem( &m_D3DParams, sizeof( D3DPRESENT_PARAMETERS ) );

		m_D3DParams.Windowed = pSettings->displaySettings.bWindowed;
		m_D3DParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
		m_D3DParams.BackBufferFormat = D3DFMT_X8R8G8B8;
		m_D3DParams.BackBufferCount = 1;
		m_D3DParams.BackBufferWidth = pSettings->displaySettings.width;
		m_D3DParams.BackBufferHeight = pSettings->displaySettings.height;
		m_D3DParams.hDeviceWindow = pEngine->GetTopLevelHWND();
		m_D3DParams.EnableAutoDepthStencil = TRUE;
		m_D3DParams.AutoDepthStencilFormat = D3DFMT_D24S8;
		
		if ( pSettings->displaySettings.bVSync )
			m_D3DParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
		else
			m_D3DParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

		// Finally create device
		m_pDevice = new RendererDevice( m_pD3D, &m_D3DParams, pEngine->GetTopLevelHWND(), 
			pSettings->displaySettings.processingType );

		m_pD3DDevice = (IDirect3DDevice9*)m_pDevice->GetDevice();

		InitializeRendering();

	}

	void Renderer::OnDeviceLost( void )
	{

	}

	void Renderer::OnDeviceReset( void )
	{

	}

	void Renderer::Reset( void )
	{
		m_D3DParams.Windowed = m_displayMode.bWindowed;
		m_D3DParams.BackBufferWidth = m_displayMode.width;
		m_D3DParams.BackBufferHeight = m_displayMode.height;

		if ( m_displayMode.bVSync )
			m_D3DParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
		else
			m_D3DParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

		HRESULT hr = 0;

		// Reset the device itself
		if ( FAILED(hr = ((IDirect3DDevice9*)m_pDevice->GetDevice())->Reset( &m_D3DParams )) )
			PSX_PushError( DX9Mapping::GetDXErrorDescription( hr ) );

	}

	void Renderer::BeginRender( void )
	{

	}

	void Renderer::Render( void )
	{
		m_pDevice->BeginRender();

		m_pDevice->EndRender();
	}

	void Renderer::EndRender( void )
	{

	}

	void Renderer::ClearBuffer( DWORD buffer )
	{
		DWORD clearFlags = 0;

		if ( buffer & EClearBufferType::DEPTH )
			clearFlags |= D3DCLEAR_ZBUFFER;
		
		if ( buffer & EClearBufferType::STENCIL )
			clearFlags |= D3DCLEAR_STENCIL;

		if ( buffer & EClearBufferType::RENDER_TARGET )
			clearFlags |= D3DCLEAR_TARGET;

		HRESULT hr;

		if ( FAILED( hr = m_pD3DDevice->Clear( 0, NULL, clearFlags, m_clearColor.dwColor, 1.0f, 0 ) ) )
		{
			PSX_PushError( DX9Mapping::GetDXErrorDescription( hr ) );
		}
	}

	void Renderer::SortRenderingStages( void )
	{
		PSX_QuickSort( m_renderStages );
	}

	void Renderer::InitializeRendering( void )
	{
		// TODO: ResetRenderStates();

		GraphicsResourceManager *pResMan = GraphicsResourceManager::GetInstance();
	
		pResMan->Initialize();

		m_pBackBuffer = pResMan->CreateTexture( PSX_String( "BackBuffer" ), m_displayMode.width, m_displayMode.height,
			ETextureType::_2D, ETextureUsage::RENDERTARGET, ETextureFormat::R8G8B8 );
	
		// TODO: Create required rendering stages.

	
	}

	void Renderer::BuildDisplayModeList( void )
	{
		SIZE_T adapterCount = m_pD3D->GetAdapterCount();

		// List all available display modes
		for ( SIZE_T i = 0; i < adapterCount; ++i )
		{
			SIZE_T numModes = m_pD3D->GetAdapterModeCount( i, D3DFMT_X8R8G8B8 );

			for ( SIZE_T j = 0; j < numModes; ++j )
			{
				D3DDISPLAYMODE mode;
				m_pD3D->EnumAdapterModes( i, D3DFMT_X8R8G8B8, j, &mode );
				DisplayMode dispMode;
				dispMode.adapter = i;
				dispMode.width = mode.Width;
				dispMode.height = mode.Height;
				m_displayModes.PushBack( dispMode );
			}
		}
	}

	void Renderer::PerformRenderStages( void )
	{

	}

}

#endif __PSX_WINDOWS_WINDOW